<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <script>
    // Function.prototype.call = function (obj, ...args) {
    //   console.log('call')
    // }

    /* 
    自定义call
    立即调用当前函数, 并指定函数中的this为第一个参数对象
    */
    function call(fn, obj, ...args) {

      // 处理obj是null/undefined情况
      if (obj===null || obj===undefined) {
        return fn(...args)
        // obj = window
      }

      // 给obj添加一个临时方法, 方法的属性值fn
      obj.tempFn = fn
      // 通过obj来调用这个方法, 并得到返回值
      const result = obj.tempFn(...args)
      // 删除添加的临时方法
      delete obj.tempFn
      // 返回这个返回值
      return result
    }

    function apply(fn, obj, args) {
      return call(fn, obj, ...args)
    }

    /* 
    返回一个新函数, 新函数内部会调用原函数, 并指定原函数内部的this为obj
    */
    function bind(fn, obj, ...args) {
      // 返回一个新函数
      return (...args2) => {
        // 函数内部会调用原函数, 并指定原函数内部的this为obj, 以及参数
        return fn.call(obj, ...args, ...args2)
      }
    }

    // 打印对象时: 默认会输出对象快照(打印时对象内部数据的情况)
  </script>

  <script>

    function fn (a, b) {
      console.log('fn', this, a, b)
      return a + b
    }

    const obj = {m: 1}
    // fn.call(obj, 2, 3)

    // console.log(call(fn, obj, 2, 3))
    // call(fn, undefined, 3, 4)

    // console.log(apply(fn, obj, [1, 3]))
    // console.log(apply(fn, undefined, [3, 5]))

    bind(fn, obj)(1, 2)
    bind(fn, obj, 3)(1, 2)
    bind(fn, obj, 3, 4)(1, 2)

  </script>
</body>
</html>